﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;

namespace Authorit.API.Configuration
{
	/// <summary>
	/// Provides properties and methods that allow plugins to read configuration settings
	/// </summary>
	/// <remarks>The <see cref="ConfigReader"/> class allows a plugin to read settings
	/// from its own app.config file instead of the default configuration file of the host application.</remarks>
	public static class ConfigReader
	{
		#region Fields (3) 

		/// <summary>
		/// File extension for application configuration files
		/// </summary>
		/// <value>".config"</value>
		private const string defaultConfigFileExtension = ".config";
		/// <summary>
		/// Default section group name
		/// </summary>
		/// <value>"applicationSettings"</value>
		private const string defaultSectionGroupName = "applicationSettings";
		/// <summary>
		/// Default section name
		/// </summary>
		/// <value>".Properties.Settings"</value>
		private const string defaultSectionNameSuffix = ".Properties.Settings";

		#endregion Fields 

		#region Properties (3) 

		/// <summary>
		/// Gets the default file extension for the configuration file
		/// </summary>
		/// <value>.config</value>
		public static string DefaultConfigFileExtension { get { return defaultConfigFileExtension; } }

		/// <summary>
		/// Gets the default section group name
		/// </summary>
		/// <seealso cref="defaultSectionGroupName"/>
		public static string DefaultSectionGroupName { get { return defaultSectionGroupName; } }

		/// <summary>
		/// Gets the default section name suffix
		/// </summary>
		/// <value>.Properties.Settings</value>
		public static string DefaultSectionNameSuffix { get { return defaultSectionNameSuffix; } }

		#endregion Properties 

		#region Methods (39) 

		// Public Methods (39) 

		/// <summary>
		/// Gets the file name of the assembly
		/// </summary>
		/// <returns>File name of the assembly</returns>
		/// <seealso cref="GetAssemblyFileName(Assembly)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static string GetAssemblyFileName()
		{
			return GetAssemblyFileName(Assembly.GetCallingAssembly());
		}

		/// <summary>
		/// Gets the file name of the assembly
		/// </summary>
		/// <param name="assembly">The assembly for which to get the file name</param>
		/// <returns>File name of the assembly</returns>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="assembly"/> or the <see cref="Assembly.CodeBase"/> property
		/// is null.</exception>
		/// <exception cref="UriFormatException">The <see cref="Assembly.CodeBase"/> property of the
		/// <paramref name="assembly"/> is not correctly formatted for the <see cref="Uri(string)"/> constructor.</exception>
		/// <see cref="Assembly.CodeBase"/>
		/// <seealso cref="Uri(string)"/>
		/// <seealso cref="Uri.LocalPath"/>
		public static string GetAssemblyFileName(Assembly assembly)
		{
			Uri assemblyUri = new Uri(assembly.CodeBase);
			return assemblyUri.LocalPath;
		}

		/// <summary>
		/// Gets the name of the assembly
		/// </summary>
		/// <returns>Name of the assembly</returns>
		/// <seealso cref="GetAssemblyName(Assembly)"/>
		public static string GetAssemblyName()
		{
			return GetAssemblyName(Assembly.GetCallingAssembly());
		}

		/// <summary>
		/// Gets the name of the assembly
		/// </summary>
		/// <param name="assembly">The assembly for which to get the name</param>
		/// <returns>Name of the assembly</returns>
		/// <seealso cref="Assembly.GetName()"/>
		/// <seealso cref="AssemblyName.Name"/>
		public static string GetAssemblyName(Assembly assembly)
		{
			return assembly.GetName().Name;
		}

		/// <summary>
		/// Gets the path to the file for the assembly
		/// </summary>
		/// <returns>Path to the file for the assembly</returns>
		/// <seealso cref="GetAssemblyPath(Assembly)"/>
		public static string GetAssemblyPath()
		{
			return GetAssemblyPath(Assembly.GetCallingAssembly());
		}

		/// <summary>
		/// Gets the path to the file for the assembly
		/// </summary>
		/// <param name="assembly">The assembly for which to get the path</param>
		/// <returns>Path to the file for the assembly</returns>
		/// <exception cref="ArgumentException">Thrown when the <see cref="Assembly.Location"/> property for the 
		/// <paramref name="assembly"/> parameter contains invalid characters, is empty, or contains only white spaces.</exception>
		/// <exception cref="PathTooLongException">Thrown when the <see cref="Assembly.Location"/> property for the
		/// <paramref name="assembly"/> parameter contains invalid characters, is empty, or contains only white spaces.</exception>
		/// <seealso cref="Assembly.Location"/>
		/// <seealso cref="Path.GetDirectoryName(string)"/>
		public static string GetAssemblyPath(Assembly assembly)
		{
			return Path.GetDirectoryName(assembly.Location);
		}

		/// <summary>
		/// Gets the configuration settings
		/// </summary>
		/// <returns>Configuration for the calling assembly</returns>
		/// <seealso cref="GetConfig(Assembly)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static System.Configuration.Configuration GetConfig()
		{
			return GetConfig(Assembly.GetCallingAssembly());
		}

		/// <summary>
		/// Gets the configuration settings
		/// </summary>
		/// <param name="assembly">The assembly for which to get the configuration settings</param>
		/// <returns>Configuration for the specified assembly</returns>
		/// <exception cref="ConfigurationErrorsException">Thrown when the configuration file cannot be loaded.</exception>
		/// <seealso cref="ExeConfigurationFileMap()"/>
		/// <seealso cref="GetConfigFileName(Assembly)"/>
		/// <seealso cref="ExeConfigurationFileMap.ExeConfigFilename"/>
		/// <seealso cref="ConfigurationManager.OpenMappedExeConfiguration(ExeConfigurationFileMap, ConfigurationUserLevel)"/>
		public static System.Configuration.Configuration GetConfig(Assembly assembly)
		{
			ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
			fileMap.ExeConfigFilename = GetConfigFileName(assembly);
			return ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
		}

		/// <summary>
		/// Gets the configuration settings
		/// </summary>
		/// <param name="configurationFile">Configuration settings files</param>
		/// <returns>Configuration from the specified file</returns>
		/// <exception cref="ConfigurationErrorsException">Thrown when the configuration file cannot be loaded.</exception>
		/// <seealso cref="ExeConfigurationFileMap()"/>
		/// <seealso cref="GetConfigFileName(Assembly)"/>
		/// <seealso cref="ExeConfigurationFileMap.ExeConfigFilename"/>
		/// <seealso cref="ConfigurationManager.OpenMappedExeConfiguration(ExeConfigurationFileMap, ConfigurationUserLevel)"/>
		public static System.Configuration.Configuration GetConfig(string configurationFile)
		{
			ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
			fileMap.ExeConfigFilename = configurationFile;
			return ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
		}

		/// <summary>
		/// Gets the file name of the configuration settings file for the assembly
		/// </summary>
		/// <returns>File name of the configuration settings file for the calling assembly</returns>
		/// <seealso cref="GetConfigFileName(Assembly)"/>
		public static string GetConfigFileName()
		{
			return GetConfigFileName(Assembly.GetCallingAssembly());
		}

		/// <summary>
		/// Gets the file name of the configuration settings file for the assembly
		/// </summary>
		/// <param name="assembly">The assembly for which to get the configuration settings file name</param>
		/// <returns>File name of the configuration settings file for the assembly</returns>
		/// <remarks>This method uses the default <see cref="defaultConfigFileExtension"/> value</remarks>
		/// <seealso cref="GetConfigFileName(Assembly, string)"/>
		public static string GetConfigFileName(Assembly assembly)
		{
			return GetConfigFileName(assembly, defaultConfigFileExtension);
		}

		/// <summary>
		/// Gets the file name of the configuration settings file for the assembly
		/// </summary>
		/// <param name="assembly">The assembly for which to get the configuration settings file name</param>
		/// <param name="configFileExtension">File extension for the configuration settings file</param>
		/// <returns>File name of the configuration settings file for the assembly</returns>
		/// <exception cref="ArgumentNullException">Thrown any of the parameters are null</exception>
		/// <exception cref="FileNotFoundException">Thrown when the configuration file does not exist</exception>
		/// <seealso cref="GetAssemblyFileName(Assembly)"/>
		/// <seealso cref="File.Exists(string)"/>
		public static string GetConfigFileName(Assembly assembly, string configFileExtension)
		{
			if (assembly == null)
			{
				throw new ArgumentNullException(ReflectionUtility.GetPropertyName(() => assembly));
			}
			if (configFileExtension == null)
			{
				throw new ArgumentNullException(ReflectionUtility.GetPropertyName(() => assembly));
			}
			string configFileName = GetAssemblyFileName(assembly) + configFileExtension;
			if (!File.Exists(configFileName))
			{
				throw new FileNotFoundException(Properties.Resources.ExceptionMessageConfigurationFileNotFound, configFileName);
			}
			return configFileName;
		}

		/// <summary>
		/// Returns the default section name for the settings in the configuration file
		/// </summary>
		/// <returns>Concatenated string made up of the name of the calling assembly 
		/// and the <see cref="DefaultSectionNameSuffix"/> value.</returns>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="GetSectionName(Assembly)"/>
		public static string GetSectionName()
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSectionName(assembly);
		}

		/// <summary>
		/// Returns the default section name for the settings in the configuration file
		/// </summary>
		/// <param name="assembly">Assembly for which to get the default section name</param>
		/// <returns>Concatenated string made up of the name of the specified assembly 
		/// and the <see cref="DefaultSectionNameSuffix"/> value.</returns>
		/// <seealso cref="GetSectionName(Assembly, string)"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		public static string GetSectionName(Assembly assembly)
		{
			return GetSectionName(assembly, defaultSectionNameSuffix);
		}

		/// <summary>
		/// Returns the default section name for the settings in the configuration file
		/// </summary>
		/// <param name="assembly">Assembly for which to get the default section name</param>
		/// <param name="sectionNameSuffix">Suffix for the section name</param>
		/// <returns>Concatenated string made up of the name of the specified assembly and the section name suffix value.</returns>
		/// <seealso cref="Assembly.GetName()"/>
		/// <seealso cref="AssemblyName.Name"/>
		/// <seealso cref="String.Concat(string, string)"/>
		public static string GetSectionName(Assembly assembly, string sectionNameSuffix)
		{
			return String.Concat(assembly.GetName().Name, sectionNameSuffix);
		}

		/// <summary>
		/// Gets the setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key for the setting</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="GetSetting(string, Assembly)"/>
		public static string GetSetting(string key)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSetting(key, assembly);
		}

		/// <summary>
		/// Gets the setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key for the setting</param>
		/// <param name="assembly">Assembly for which to read the setting</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="GetSetting(string, Assembly, string, string)"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		public static string GetSetting(string key, Assembly assembly)
		{
			return GetSetting(key, assembly, defaultSectionGroupName, GetSettingSectionName(assembly, defaultSectionNameSuffix));
		}

		/// <summary>
		/// Gets the setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key for the setting</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="GetSetting(string, Assembly, string, string)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static string GetSetting(string key, string sectionGroup, string sectionName)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSetting(key, assembly, sectionGroup, sectionName);
		}

		/// <summary>
		/// Gets the setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key for the setting</param>
		/// <param name="assembly">Assembly for which to get the setting</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="XmlNode.InnerText"/>
		/// <seealso cref="GetSettingXml(string, Assembly, string, string)"/>
		public static string GetSetting(string key, Assembly assembly, string sectionGroup, string sectionName)
		{
			string returnValue;
			XmlNode settingXml = (XmlNode)GetSettingXml(key, assembly, sectionGroup, sectionName);
			returnValue = settingXml.InnerText;
			return returnValue;
		}

		/// <summary>
		/// Gets the Boolean setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="GetSettingAsBoolean(string, Assembly)"/>
		public static bool GetSettingAsBoolean(string key)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsBoolean(key, assembly);
		}

		/// <summary>
		/// Gets the Boolean setting value for the specified key
		/// </summary>
		/// <param name="key"></param>
		/// <param name="assembly">Assembly for which to get the setting</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="GetSettingAsBoolean(string, Assembly, string, string)"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		/// <seealso cref="GetSettingSectionName(Assembly, string)"/>
		public static bool GetSettingAsBoolean(string key, Assembly assembly)
		{
			return GetSettingAsBoolean(key, assembly, defaultSectionGroupName, GetSettingSectionName(assembly, defaultSectionNameSuffix));
		}

		/// <summary>
		/// Gets the Boolean setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="GetSettingAsBoolean(string, Assembly, string, string)"/>
		public static bool GetSettingAsBoolean(string key, string sectionGroup, string sectionName)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsBoolean(key, assembly, sectionGroup, sectionName);
		}

		/// <summary>
		/// Gets the Boolean setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to get the setting</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <exception cref="ArgumentNullException">Thrown when the setting value is null.</exception>
		/// <exception cref="FormatException">Thrown when the setting value is not equivalent to 
		/// the value of the <see cref="Boolean.TrueString"/> or <see cref="Boolean.FalseString"/> field.</exception>
		/// <seealso cref="GetSetting(string, Assembly, string, string)"/>
		/// <seealso cref="bool.Parse(string)"/>
		public static bool GetSettingAsBoolean(string key, Assembly assembly, string sectionGroup, string sectionName)
		{
			string val = GetSetting(key, assembly, sectionGroup, sectionName);
			bool returnValue = bool.Parse(val);
			return returnValue;
		}

		/// <summary>
		/// Gets the integer setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="GetSettingAsInt32(string, Assembly)"/>
		public static int GetSettingAsInt32(string key)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsInt32(key, assembly);
		}

		/// <summary>
		/// Gets the integer setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to get the setting</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="GetSettingAsInt32(string, Assembly, string, string)"/>
		/// <seealso cref="DefaultSectionGroupName"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		public static int GetSettingAsInt32(string key, Assembly assembly)
		{
			return GetSettingAsInt32(key, assembly, defaultSectionGroupName, GetSectionName(assembly, defaultSectionNameSuffix));
		}

		/// <summary>
		/// Gets the integer setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="GetSettingAsInt32(string, Assembly, string, string)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static int GetSettingAsInt32(string key, string sectionGroup, string sectionName)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsInt32(key, assembly, sectionGroup, sectionName);
		}

		/// <summary>
		/// Gets the integer setting value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to get the setting</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <exception cref="ArgumentNullException">Thrown when the setting value is null.</exception>
		/// <exception cref="FormatException">Thrown when the setting value is not of the correct format.</exception>
		/// <exception cref="OverflowException">Thrown when the setting value represents a number 
		/// less than <see cref="Int32.MinValue"/> or greater than <see cref="Int32.MaxValue"/>.</exception>
		/// <seealso cref="GetSetting(string, Assembly, string, string)"/>
		/// <seealso cref="Int32.Parse(string, IFormatProvider)"/>
		/// <seealso cref="CultureInfo.InvariantCulture"/>
		public static int GetSettingAsInt32(string key, Assembly assembly, string sectionGroup, string sectionName)
		{
			string settingValue = GetSetting(key, assembly, sectionGroup, sectionName);
			int returnValue = Int32.Parse(settingValue, CultureInfo.InvariantCulture);
			return returnValue;
		}

		/// <summary>
		/// Gets a read only string collection of values for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <returns>Value of the setting</returns>
		/// <remarks>This method gets the setting for the calling assembly.</remarks>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		/// <seealso cref="GetSettingSectionName(Assembly, string)"/>
		/// <seealso cref="DefaultSectionGroupName"/>
		/// <seealso cref="GetSettingAsReadOnlyStringCollection(string, Assembly, string, string)"/>
		public static ReadOnlyCollection<string> GetSettingAsReadOnlyStringCollection(string key)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsReadOnlyStringCollection(key, assembly, defaultSectionGroupName, GetSettingSectionName(assembly, defaultSectionNameSuffix));
		}

		/// <summary>
		/// Gets a read only string collection of values for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <remarks>This method finds the setting for the calling assembly.</remarks>
		/// <seealso cref="GetSettingAsStringCollection(string, Assembly, string, string)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static ReadOnlyCollection<string> GetSettingAsReadOnlyStringCollection(string key, string sectionGroup, string sectionName)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsReadOnlyStringCollection(key, assembly, sectionGroup, sectionName);
		}

		/// <summary>
		/// Gets a read only string collection of values for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to get the setting</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <exception cref="ArgumentNullException">Thrown when a parameter or the setting value is null.</exception>
		/// <exception cref="InvalidCastException">Thrown when an element in the sequence cannot be cast to the type <see cref="String"/>.</exception>
		/// <seealso cref="ReadOnlyCollection{T}(IList{T})"/>
		/// <seealso cref="IEnumerable"/>
		public static ReadOnlyCollection<string> GetSettingAsReadOnlyStringCollection(string key, Assembly assembly, string sectionGroup, string sectionName)
		{
			StringCollection settingValue = GetSettingAsStringCollection(key, assembly, sectionGroup, sectionName);
			return new ReadOnlyCollection<string>(settingValue.Cast<string>().ToList());
		}

		/// <summary>
		/// Gets the string collection value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <returns>Value of the setting</returns>
		/// <remarks>This method gets the setting for the calling assembly</remarks>
		/// <seealso cref="GetSettingAsStringCollection(string, Assembly, string, string)"/>
		/// <seealso cref="GetSettingSectionName(Assembly, string)"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static StringCollection GetSettingAsStringCollection(string key)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsStringCollection(key, assembly, defaultSectionGroupName, GetSettingSectionName(assembly, defaultSectionNameSuffix));
		}

		/// <summary>
		/// Gets the string collection value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="GetSettingAsStringCollection(string, Assembly, string, string)"/>
		public static StringCollection GetSettingAsStringCollection(string key, string sectionGroup, string sectionName)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingAsStringCollection(key, assembly, sectionGroup, sectionName);
		}

		/// <summary>
		/// Gets the string collection value for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to get the value</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <remarks>The method selects nodes in the XML using the XPath expression "ArrayOfString/string".</remarks>
		/// <exception cref="XPathException">Thrown when the XPath expression contains a prefix.</exception>
		/// <seealso cref="GetSettingXml(string, Assembly, string, string)"/>
		/// <seealso cref="XmlNode.SelectNodes(string)"/>
		/// <seealso cref="StringCollection.Add(string)"/>
		/// <seealso cref="XmlNode.InnerText"/>
		public static StringCollection GetSettingAsStringCollection(string key, Assembly assembly, string sectionGroup, string sectionName)
		{
			XmlNode valXml = (XmlNode)GetSettingXml(key, assembly, sectionGroup, sectionName);
			StringCollection returnVal = new StringCollection();
			foreach (XmlNode childNode in valXml.SelectNodes("ArrayOfString/string"))
			{
				returnVal.Add(childNode.InnerText);
			}
			return returnVal;
		}

		/// <summary>
		/// Gets the name of the settings section in the configuration file
		/// </summary>
		/// <returns>Name of the settings section element</returns>
		/// <remarks>This method uses the calling assembly and default section name suffix.</remarks>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="Assembly.GetName()"/>
		/// <seealso cref="AssemblyName.Name"/>
		/// <see cref="DefaultSectionNameSuffix"/>
		/// <seealso cref="String.Concat(string, string)"/>
		public static string GetSettingSectionName()
		{
			return String.Concat(Assembly.GetCallingAssembly().GetName().Name, defaultSectionNameSuffix);
		}

		/// <summary>
		/// Gets the name of the settings section in the configuration file
		/// </summary>
		/// <param name="assembly">Assembly for which to get the settings section name</param>
		/// <param name="sectionNameSuffix">Suffix for the settings section name</param>
		/// <returns>Name of the settings section element</returns>
		/// <seealso cref="Assembly.GetName()"/>
		/// <seealso cref="AssemblyName.Name"/>
		/// <seealso cref="String.Concat(string, string)"/>
		public static string GetSettingSectionName(Assembly assembly, string sectionNameSuffix)
		{
			return String.Concat(assembly.GetName().Name, sectionNameSuffix);
		}

		/// <summary>
		/// Gets the XML for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <returns>XML for the setting</returns>
		/// <seealso cref="GetSettingXml(string, Assembly)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		/// <seealso cref="DefaultSectionGroupName"/>
		/// <seealso cref="GetSettingSectionName(Assembly, string)"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		public static IXPathNavigable GetSettingXml(string key)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingXml(key, assembly);
		}

		/// <summary>
		/// Gets the XML for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to read the XML</param>
		/// <returns>XML for the setting</returns>
		/// <seealso cref="GetSettingXml(string, Assembly, string, string)"/>
		/// <seealso cref="GetSettingSectionName(Assembly, string)"/>
		/// <seealso cref="DefaultSectionNameSuffix"/>
		public static IXPathNavigable GetSettingXml(string key, Assembly assembly)
		{
			return GetSettingXml(key, assembly, defaultSectionGroupName, GetSettingSectionName(assembly, defaultSectionNameSuffix));
		}

		/// <summary>
		/// Gets the XML for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="GetSettingXml(string, Assembly, string, string)"/>
		/// <seealso cref="Assembly.GetCallingAssembly()"/>
		public static IXPathNavigable GetSettingXml(string key, string sectionGroup, string sectionName)
		{
			Assembly assembly = Assembly.GetCallingAssembly();
			return GetSettingXml(key, assembly, sectionGroup, sectionName);
		}

		/// <summary>
		/// Gets the XML for the specified key
		/// </summary>
		/// <param name="key">Name of the key</param>
		/// <param name="assembly">Assembly for which to get the XML</param>
		/// <param name="sectionGroup">Name of the section group</param>
		/// <param name="sectionName">Name of the section</param>
		/// <returns>Value of the setting</returns>
		/// <seealso cref="ClientSettingsSection"/>
		/// <seealso cref="GetConfig(Assembly)"/>
		/// <seealso cref="System.Configuration.Configuration.GetSection(string)"/>
		/// <seealso cref="ClientSettingsSection.Settings"/>
		/// <seealso cref="SettingElementCollection.Get(string)"/>
		/// <seealso cref="SettingsSerializeAs"/>
		/// <seealso cref="SettingValueElement.ValueXml"/>
		/// <seealso cref="XmlNode"/>
		public static IXPathNavigable GetSettingXml(string key, Assembly assembly, string sectionGroup, string sectionName)
		{
			System.Configuration.Configuration config = GetConfig(assembly);
			string sectionPath = String.Concat(sectionGroup, "/", sectionName);
			ClientSettingsSection appSettings = (ClientSettingsSection)config.GetSection(sectionPath);
			SettingElement thisSettingElement = appSettings.Settings.Get(key);
			return thisSettingElement.Value.ValueXml;
		}

		#endregion Methods 
	}

	/// <summary>
	/// Contains a method that provides information about types and their members.
	/// </summary>
	/// <remarks>See http://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/ for more information.</remarks>
	public static class ReflectionUtility
	{
		#region Methods (1) 

		// Public Methods (1) 

		/// <summary>
		/// Gets the name of the specified property.
		/// </summary>
		/// <param name="expression">Lambda expression for the member.</param>
		/// <returns>string value that contains the name of the property</returns>
		/// <example>
		/// The following example demonstrates using the method to get the name of 
		/// the property MyProperty in the class myClass.
		/// <code>string propertyName = ReflectionUtility.GetPropertyName(() => myClass.MyProperty;</code>
		/// </example>
		/// <seealso cref="MemberExpression.Member"/>
		/// <seealso cref="MemberInfo.Name"/>
		public static string GetPropertyName<T>(Expression<Func<T>> expression)
		{
			MemberExpression body = (MemberExpression)expression.Body;  //Get the Linq expression that represents the member body
			return body.Member.Name;  //Return the name of the member
		}

		#endregion Methods 
	}
}